package org.study.redisson.boot.util;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

import java.util.concurrent.TimeUnit;

/**
 * Redisson 实现分布式锁
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2021-06-03
 */
@Slf4j
public class RedissonLockUtils {
    private static RedissonClient redissonClient;

    public static void setRedissonClient(RedissonClient client) {
        RedissonLockUtils.redissonClient = client;
    }

    /**
     * 获取锁，如果锁不可用，则当前线程处于休眠状态，直到获得锁为止。
     *
     * @param lockKey
     */
    public static void lock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock();
        log.info("当前线程：{}，获取锁lockKey：{}", Thread.currentThread().getName(), lockKey);
    }

    /**
     * 获取锁，如果锁不可用，则当前线程处于休眠状态，直到获得锁为止。
     * 如果获取到锁后，执行结束或达到超时时间(单位是秒)后会自动释放锁。
     *
     * @param lockKey
     * @param timeout 超时时间
     */
    public static void lock(String lockKey, long timeout) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, TimeUnit.SECONDS);
        log.info("当前线程：{}，获取锁lockKey：{}", Thread.currentThread().getName(), lockKey);
    }

    /**
     * 获取锁，如果锁不可用，则当前线程处于休眠状态，直到获得锁为止。
     * 如果获取到锁后，执行结束或达到超时时间后会自动释放锁。
     *
     * @param lockKey
     * @param timeout 超时时间
     * @param unit    时间单位
     */
    public static void lock(String lockKey, long timeout, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(timeout, unit);
        log.info("当前线程：{}，获取锁lockKey：{}", Thread.currentThread().getName(), lockKey);
    }

    /**
     * 尝试获取锁，未获取到立即返回false，获取到立即返回true
     *
     * @param lockKey
     * @return
     */
    public static boolean tryLock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        boolean flag = lock.tryLock();
        log.info("当前线程：{}，尝试获取锁lockKey：{}，结果：{}", Thread.currentThread().getName(), lockKey, flag);
        return flag;
    }

    /**
     * 尝试获取锁，未获取到立即返回false，获取到立即返回true且在超时时间(单位是秒)后自动释放锁
     *
     * @param lockKey
     * @param timeout 超时时间
     * @return
     */
    public static boolean tryLock(String lockKey, long timeout) {
        boolean flag = false;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            flag = lock.tryLock(timeout, TimeUnit.SECONDS);
            log.info("当前线程：{}，尝试获取锁lockKey：{}，结果：{}", Thread.currentThread().getName(), lockKey, flag);
        } catch (InterruptedException e) {
            log.error("当前线程：{}，尝试获取锁lockKey：{}，异常：{}", Thread.currentThread().getName(), lockKey, e);
        }
        return flag;
    }

    /**
     * 尝试获取锁，未获取到立即返回false，获取到立即返回true且在指定时间后自动释放锁
     *
     * @param lockKey
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return
     */
    public static boolean tryLock(String lockKey, long timeout, TimeUnit unit) {
        boolean flag = false;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            flag = lock.tryLock(timeout, unit);
            log.info("当前线程：{}，尝试获取锁lockKey：{}，结果：{}", Thread.currentThread().getName(), lockKey, flag);
        } catch (InterruptedException e) {
            log.error("当前线程：{}，尝试获取锁lockKey：{}，异常：{}", Thread.currentThread().getName(), lockKey, e);
        }
        return flag;
    }

    /**
     * 尝试获取锁，在等待时间内未获取到锁返回false，获取到锁则返回true。
     * 如果获取到锁后，执行结束或达到超时时间(单位是秒)后会自动释放锁。
     *
     * @param lockKey
     * @param waitTime 最多等待时间
     * @param timeout  超时时间
     * @return
     */
    public static boolean tryLock(String lockKey, long waitTime, long timeout) {
        boolean flag = false;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            flag = lock.tryLock(waitTime, timeout, TimeUnit.SECONDS);
            log.info("当前线程：{}，尝试获取锁lockKey：{}，结果：{}", Thread.currentThread().getName(), lockKey, flag);
        } catch (InterruptedException e) {
            log.error("当前线程：{}，尝试获取锁lockKey：{}，异常：{}", Thread.currentThread().getName(), lockKey, e);
        }
        return flag;
    }

    /**
     * 尝试获取锁，在等待时间内未获取到锁返回false，获取到锁则返回true。
     * 如果获取到锁后，执行结束或达到超时时间后会自动释放锁。
     *
     * @param lockKey
     * @param waitTime 最多等待时间
     * @param timeout  超时时间
     * @param unit     时间单位
     * @return
     */
    public static boolean tryLock(String lockKey, long waitTime, long timeout, TimeUnit unit) {
        boolean flag = false;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            flag = lock.tryLock(waitTime, timeout, unit);
            log.info("当前线程：{}，尝试获取锁lockKey：{}，结果：{}", Thread.currentThread().getName(), lockKey, flag);
        } catch (InterruptedException e) {
            log.error("当前线程：{}，尝试获取锁lockKey：{}，异常：{}", Thread.currentThread().getName(), lockKey, e);
        }
        return flag;
    }

    /**
     * 释放锁
     *
     * @param lockKey
     */
    public static void unlock(String lockKey) {
        RLock lock = redissonClient.getLock(lockKey);
        if (lock.isHeldByCurrentThread()) {
            // 如果锁被当前线程持有，则释放
            lock.unlock();
            log.info("当前线程：{}，释放锁lockKey：{}", Thread.currentThread().getName(), lockKey);
        }
    }
}
